package com.example.base.base

import androidx.annotation.MainThread
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.base.base.BaseViewModel.*
import com.example.base.eventlivedata.EventLiveData
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch

/**
 *  仅支持 VmLiveData!!!
 */
typealias VmLiveData<T> = EventLiveData<VmState<T>>

open class BaseViewModel : ViewModel() {

    sealed class VmState<out T> {
        object Before : VmState<Nothing>()
        data class Result<out T>(val data: T) : VmState<T>()
        data class Error(val error: Throwable) : VmState<Nothing>()
    }

    class VmResult<T> {
        var onBefore: (() -> Unit)? = null
        var onResult: ((T) -> Unit)? = null  //等同Task的success和progress,这里2合一了t
        var onError: ((Throwable) -> Unit)? = null
        var onComplete: (() -> Unit)? = null
    }

    inline fun <RESULT> VmLiveData<RESULT>.vmTask(crossinline block: suspend (VmLiveData<RESULT>) -> RESULT) {

        viewModelScope.launch {
            runCatching {
                this@vmTask.submitValue(VmState.Before)
                return@runCatching block.invoke(this@vmTask)
            }.onSuccess {
                this@vmTask.submitValue(VmState.Result(it))
            }.onFailure {
                this@vmTask.submitValue(VmState.Error(it))
            }
        }
    }
}

/**
 *  使用协程执行 -- 不俘获异常
 */
fun BaseViewModel.launchBlock(block: suspend () -> Unit): Job {
    return viewModelScope.launch {
        block.invoke()
    }
}

@MainThread
inline fun <T> VmLiveData<T>.vmObserver(owner: LifecycleOwner, vmResult: VmResult<T>.() -> Unit) {
    val result = VmResult<T>()
    result.vmResult()
    this.observe(owner, Observer {
        when (it) {
            is VmState.Before -> result.onBefore?.invoke()
            is VmState.Result -> {
                result.onResult?.invoke(it.data)
                result.onComplete?.invoke()
            }
            is VmState.Error -> {
                result.onError?.invoke(it.error)
                result.onComplete?.invoke()
            }
        }
    })
}


